﻿// -----------------------------------------------------------------------
// <copyright file="PushApplicationRepository.cs" company="Zen Design Corp">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace Zen.BlackBerry.Push.Repositories
{
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using Zen.BlackBerry.Push.AppManagement;
	using Zen.BlackBerry.Push.DataAccess;

	/// <summary>
	/// TODO: Update summary.
	/// </summary>
	public class PushApplicationRepository : Repository, IPushApplicationRepository
	{
		private IEnumerable<PushApplication> Populate(IQueryable<DbPushApplication> query)
		{
			return query
				.ToArray()
				.Select((item) =>
					new PushApplication
					{
						Id = item.PushApplicationId,
						RimApplicationId = item.RimApplicationId,
						Type = (PushApplicationType)item.ApplicationType,
						AutoDeleteRequests = item.AutoDeleteRequests,
						BypassSubscriptions = item.BypassSubscriptions,
						CreatedDate = item.CreatedDate,
						DefaultPushLiveTime = TimeSpan.FromSeconds(item.DefaultPushTtl),
						Description = item.Description,
						EnterpriseNotifyUrl = item.EnterpriseNotifyUrl,
						EnterprisePassword = item.EnterprisePassword,
						ExpiryDate = item.ExpiryDate,
						IsApplicationReliable = item.IsApplicationReliable,
						Status = item.IsDisabled ? PushApplicationStatus.Inactive : PushApplicationStatus.Active,
						IsLastNotificationEnabled = item.IsLastNotificationEnabled,
						IsStorePushRequests = item.IsStoringPushRequests,
						ModifiedBy = item.LastUpdateBy,
						ModifiedDate = item.LastUpdateDate,
						MaximumDailyPushes = item.MaximumDailyPushes,
						Name = item.Name,
						PublicNotifyUrl = item.PublicNotifyUrl,
						PublicPassword = item.PublicPassword,
						PushPort = item.PushPort,
						ServiceLevel = (PushApplicationServiceLevel)item.ServiceLevel,
						StartDate = item.StartDate,
						UserName = item.UserName,
						Version = item.Version
					});
		}

		#region IPushApplicationRepository Members

		public void AddPushApplication(PushApplication application)
		{
			DateTime utcNow = DateTime.UtcNow;
			DbPushApplication DbPushApplication =
				new DbPushApplication
				{
					PushApplicationId = application.Id,
					RimApplicationId = application.RimApplicationId,
					ApplicationType = (short)application.Type,
					AutoDeleteRequests = application.AutoDeleteRequests,
					BypassSubscriptions = application.BypassSubscriptions,
					CreatedDate = utcNow,
					DefaultPushTtl = (int)application.DefaultPushLiveTime.TotalSeconds,
					Description = application.Description,
					EnterpriseNotifyUrl = application.EnterpriseNotifyUrl,
					EnterprisePassword = application.EnterprisePassword,
					ExpiryDate = application.ExpiryDate,
					IsApplicationReliable = application.IsApplicationReliable,
					IsDisabled = application.Status == PushApplicationStatus.Inactive,
					IsLastNotificationEnabled = application.IsLastNotificationEnabled,
					IsStoringPushRequests = application.IsStorePushRequests,
					LastUpdateBy = application.ModifiedBy,
					LastUpdateDate = utcNow,
					MaximumDailyPushes = application.MaximumDailyPushes,
					Name = application.Name,
					PublicNotifyUrl = application.PublicNotifyUrl,
					PublicPassword = application.PublicPassword,
					PushPort = application.PushPort,
					ServiceLevel = (short)application.ServiceLevel,
					StartDate = application.StartDate,
					UserName = application.UserName,
					Version = application.Version
				};

			ObjectContext.DbPushApplications.AddObject(DbPushApplication);
			try
			{
				ObjectContext.SaveChanges();
			}
			catch
			{
				ObjectContext.DbPushApplications.DeleteObject(DbPushApplication);
				throw;
			}
		}

		public void ChangePushApplicationEnterpriseCredentials(string id, string currentUsername, string newUsername, string currentPassword, string newPassword, string modifiedBy)
		{
			DbPushApplication DbPushApplication = ObjectContext.DbPushApplications.First((item) => item.RimApplicationId == id);
			if (DbPushApplication.ApplicationType == (short)PushApplicationType.PublicPush)
			{
				throw new InvalidOperationException("Cannot set enterprise credentials for application of type PUBLIC.");
			}

			// Username and password need to match
			if (DbPushApplication.UserName == currentUsername && DbPushApplication.EnterprisePassword == currentPassword)
			{
				DbPushApplication.UserName = newUsername;
				DbPushApplication.EnterprisePassword = newPassword;
				DbPushApplication.LastUpdateBy = modifiedBy;
				DbPushApplication.LastUpdateDate = DateTime.UtcNow;

				// TODO: Add log record

				ObjectContext.SaveChanges();
			}
		}

		public void ChangePushApplicationPublicCredentials(string id, string currentPassword, string newPassword, string modifiedBy)
		{
			DbPushApplication DbPushApplication = ObjectContext.DbPushApplications.First((item) => item.RimApplicationId == id);
			if (DbPushApplication.ApplicationType == (short)PushApplicationType.EnterprisePush)
			{
				throw new InvalidOperationException("Cannot set enterprise credentials for application of type ENTERPRISE.");
			}

			// Username and password need to match
			if (DbPushApplication.PublicPassword == currentPassword)
			{
				DbPushApplication.PublicPassword = newPassword;
				DbPushApplication.LastUpdateBy = modifiedBy;
				DbPushApplication.LastUpdateDate = DateTime.UtcNow;

				// TODO: Add log record

				ObjectContext.SaveChanges();
			}
		}

		public void ChangePushApplicationTypeFromEnterpriseToPublicAndEnterprise(string id, string publicPassword, string publicNotifyUrl, string modifiedBy)
		{
			DbPushApplication DbPushApplication = ObjectContext.DbPushApplications.First((item) => item.RimApplicationId == id);
			if (DbPushApplication.ApplicationType != (short)PushApplicationType.EnterprisePush)
			{
				throw new InvalidOperationException("Application type must be ENTERPRISE.");
			}

			DbPushApplication.ApplicationType = (short)PushApplicationType.PublicEnterprisePush;
			DbPushApplication.PublicPassword = publicPassword;
			DbPushApplication.PublicNotifyUrl = publicNotifyUrl;
			DbPushApplication.LastUpdateBy = modifiedBy;
			DbPushApplication.LastUpdateDate = DateTime.UtcNow;

			// TODO: Add log record

			ObjectContext.SaveChanges();
		}

		public void ChangePushApplicationTypeFromPublicToPublicAndEnterprise(string id, string enterpriseUsername, string enterprisePassword, string enterpriseNotifyUrl, string modifiedBy)
		{
			DbPushApplication DbPushApplication = ObjectContext.DbPushApplications.First((item) => item.RimApplicationId == id);
			if (DbPushApplication.ApplicationType != (short)PushApplicationType.PublicPush)
			{
				throw new InvalidOperationException("Application type must be PUBLIC.");
			}

			DbPushApplication.ApplicationType = (short)PushApplicationType.PublicEnterprisePush;
			DbPushApplication.UserName = enterpriseUsername;
			DbPushApplication.EnterprisePassword = enterprisePassword;
			DbPushApplication.EnterpriseNotifyUrl = enterpriseNotifyUrl;
			DbPushApplication.LastUpdateBy = modifiedBy;
			DbPushApplication.LastUpdateDate = DateTime.UtcNow;

			// TODO: Add log record

			ObjectContext.SaveChanges();
		}

		public void DeletePushApplication(string id, string deletedBy)
		{
			DbPushApplication DbPushApplication = ObjectContext.DbPushApplications.First((item) => item.RimApplicationId == id);
			ObjectContext.DbPushApplications.DeleteObject(DbPushApplication);

			// TODO: Add log record

			ObjectContext.SaveChanges();
		}

		public void DisablePushApplication(string id, string modifiedBy)
		{
			DbPushApplication DbPushApplication = ObjectContext.DbPushApplications.First((item) => item.RimApplicationId == id);
			if (!DbPushApplication.IsDisabled)
			{
				DbPushApplication.IsDisabled = true;
				DbPushApplication.LastUpdateBy = modifiedBy;
				DbPushApplication.LastUpdateDate = DateTime.UtcNow;

				// TODO: Add log record

				ObjectContext.SaveChanges();
			}
		}

		public void EnablePushApplication(string id, string modifiedBy)
		{
			DbPushApplication DbPushApplication = ObjectContext.DbPushApplications.First((item) => item.RimApplicationId == id);
			if (!DbPushApplication.IsDisabled)
			{
				DbPushApplication.IsDisabled = false;
				DbPushApplication.ExpiryDate = null;
				DbPushApplication.LastUpdateBy = modifiedBy;
				DbPushApplication.LastUpdateDate = DateTime.UtcNow;

				// TODO: Add log record

				ObjectContext.SaveChanges();
			}
		}

		public void EnablePushApplication(string id, DateTime expiryDate, string modifiedBy)
		{
			DbPushApplication DbPushApplication = ObjectContext.DbPushApplications.First((item) => item.RimApplicationId == id);
			if (!DbPushApplication.IsDisabled)
			{
				DbPushApplication.IsDisabled = false;
				DbPushApplication.ExpiryDate = expiryDate;
				DbPushApplication.LastUpdateBy = modifiedBy;
				DbPushApplication.LastUpdateDate = DateTime.UtcNow;

				// TODO: Add log record

				ObjectContext.SaveChanges();
			}
		}

		public IEnumerable<PushApplication> FindAll()
		{
			IQueryable<DbPushApplication> query = ObjectContext.DbPushApplications;
			return Populate(query);
		}

		public PushApplication FindById(Guid id)
		{
			IQueryable<DbPushApplication> query = ObjectContext.DbPushApplications
				.Where((item) => item.PushApplicationId == id);
			return Populate(query).FirstOrDefault();
		}

		public PushApplication FindByAppId(string id)
		{
			IQueryable<DbPushApplication> query = ObjectContext.DbPushApplications
				.Where((item) => item.RimApplicationId == id);
			return Populate(query).FirstOrDefault();
		}

		public PushApplication FindByName(string name)
		{
			IQueryable<DbPushApplication> query = ObjectContext.DbPushApplications
				.Where((item) => item.Name == name);
			return Populate(query).FirstOrDefault();
		}

		public IEnumerable<PushApplication> FindByStatus(PushApplicationStatus status)
		{
			bool isDisabled = (status == PushApplicationStatus.Inactive);
			IQueryable<DbPushApplication> query = ObjectContext.DbPushApplications
				.Where((item) => item.IsDisabled == isDisabled);
			return Populate(query);
		}

		public IEnumerable<PushApplication> FindByType(PushApplicationType type)
		{
			IQueryable<DbPushApplication> query = ObjectContext.DbPushApplications
				.Where((item) => item.ApplicationType == (short)type);
			return Populate(query);
		}

		public void UpdatePushApplication(PushApplication application)
		{
			DbPushApplication DbPushApplication = ObjectContext.DbPushApplications.First((item) => item.RimApplicationId == application.RimApplicationId);

			// Validate no change to credentials
			if ((!string.IsNullOrEmpty(application.UserName) && DbPushApplication.UserName != application.UserName) ||
				(!string.IsNullOrEmpty(application.PublicPassword) && DbPushApplication.PublicPassword != application.PublicPassword) ||
				(!string.IsNullOrEmpty(application.EnterprisePassword) && DbPushApplication.EnterprisePassword != application.EnterprisePassword))
			{
				throw new InvalidOperationException("Cannot change credentials.");
			}

			// Validate name change is unique
			if (DbPushApplication.Name != application.Name)
			{
				DbPushApplication dbUniqueName = ObjectContext.DbPushApplications.FirstOrDefault((item) => item.Name == application.Name);
				if (dbUniqueName != null)
				{
					throw new ArgumentException("Application name is already in use.");
				}
			}

			// Serialize changes to Push Plus specific features
			DbPushApplication.AutoDeleteRequests = application.AutoDeleteRequests;
			DbPushApplication.BypassSubscriptions = application.BypassSubscriptions;
			DbPushApplication.DefaultPushTtl = (int)application.DefaultPushLiveTime.TotalSeconds;
			DbPushApplication.Description = application.Description;
			DbPushApplication.EnterpriseNotifyUrl = application.EnterpriseNotifyUrl;
			DbPushApplication.ExpiryDate = application.ExpiryDate;
			DbPushApplication.IsApplicationReliable = application.IsApplicationReliable;
			DbPushApplication.IsDisabled = application.Status == PushApplicationStatus.Inactive;
			DbPushApplication.IsLastNotificationEnabled = application.IsLastNotificationEnabled;
			DbPushApplication.IsStoringPushRequests = application.IsStorePushRequests;
			DbPushApplication.LastUpdateBy = application.ModifiedBy;
			DbPushApplication.LastUpdateDate = DateTime.UtcNow;
			DbPushApplication.MaximumDailyPushes = application.MaximumDailyPushes;
			DbPushApplication.Name = application.Name;
			DbPushApplication.PublicNotifyUrl = application.PublicNotifyUrl;
			DbPushApplication.PushPort = application.PushPort;
			DbPushApplication.ServiceLevel = (short)application.ServiceLevel;
			DbPushApplication.StartDate = application.StartDate;
			DbPushApplication.Version = application.Version;

			// TODO: Add log record

			ObjectContext.SaveChanges();
		}

		#endregion
	}
}
